/** @file filmbaza.cpp */
#include <cstring>
#include <strings.h>

#include "filmbaza.h"
#include "list.h"
#include "defines.h"

/**
 * @brief get_next_id szukaj następnego wolnego numeru id w bazie filmów
 * @param fb wskaźnik do bazy danych
 * @return id
 */
static int get_next_id(struct filmbaza *fb)
{
    struct film_s **head;
	struct film_s *film = NULL;
	int id;
	
	if ( !fb ) return -1;
	head = &fb->filmy;
	
	
    for (id = 0;; id++) {
		
        list_for_each(film, head) {
            if ( id == film->id )
				break;
		}
		
        if ( !film )
			break;
	}
	
	return id;
}
/**
 * @brief filmbaza_find_film_id znajduje film_s na podstawie podanego id
 * @param fb wskaźnik do bazy filmów
 * @param id id
 * @return wskaźnik do film_s lub NULL, jeśli nie znaleziono
 */
film_s *filmbaza_find_film_id(struct filmbaza *fb, int id)
{
    struct film_s **head;
	struct film_s *film;
	
	if ( !fb ) return NULL;
	head = &fb->filmy;
	
    list_for_each(film, head) {
		if ( film->id == id )
			break;
	}
	
    return film;
}
/**
 * @brief filmbaza_create tworzy baze filmów
 * @param name nazwa bazy filmów
 * @return wskaźnik do bazy filmów
 */
filmbaza *filmbaza_create(std::string name)
{
	struct filmbaza *fb;
	
	fb = new struct filmbaza;
	if ( !fb )
		goto error_fb_new;

    fb->name.clear();
    fb->name = name;
    fb->filmy = NULL;

	return fb;
	
error_fb_new:
	PERROR("Cannot allocate memory");
	return NULL;
}
/**
 * @brief filmbaza_destroy usuwa baze flimów
 * @param fb wskaźnik do bazy filmów
 */
void filmbaza_destroy(struct filmbaza *fb)
{
	filmbaza_delete_all_films(fb);
	delete fb;
}
/**
 * @brief dodaje film do bazy filmów
 * @param fb wskaźnik do bazy danych filmów
 * @param name structura filmu do dodania
 * @return 0 jeśli sukces, -1 jeśli niepowodzenie.
 */
int filmbaza_add(struct filmbaza *fb, film_s film)
{
    struct film_s **head;
    struct film_s *pnt;
	
	if ( !fb ) return -1;
	head = &fb->filmy;
	
	pnt = new struct film_s;
	if ( !pnt )
		goto error_pnt_new;
		
	/* copy info */
	*pnt = film;
	pnt->id = get_next_id(fb);
	
	/* add to the list */
    list_add(pnt, head);
	
	return 0;
	
error_pnt_new:
	PERROR("Cannot allocate memory");
	return -1;
}

/**
 * @brief dodaje film do bazy filmóœ o podanej nazwie
 * @param fb wskaźnik do bazy danych filmów
 * @param name nazwa filmu do dodania
 * @return 0 jeśli sukces, -1 jeśli niepowodzenie.
 */
int filmbaza_add(struct filmbaza *fb, std::string name)
{
	film_s film = {
		0,
		name, /* plum */
		0,
		0,
		"",
        NULL,
	};
	return filmbaza_add(fb, film);
}
/**
 * @brief usuwa film o podanym id
 * @param fb wskaźńik do bazy danych filmów
 * @param id id
 */
void filmbaza_delete(struct filmbaza *fb, int id) 
{
    film_s **head;
	film_s *film;
	if ( !fb ) 
		return;
    if ( !fb->filmy )
           return;
    head = &fb->filmy;

	film = filmbaza_find_film_id(fb, id);
	if ( !film ) 
		return;
	
    list_del(film, head);
	delete film;
}
/**
 * @brief filmbaza_delete_all_films usuwa wszystkie filmy z bazy filmów
 * @param fb wskaźnik do bazy filmów
 */
void filmbaza_delete_all_films(struct filmbaza *fb)
{
    struct film_s **head;
    struct film_s *pnt;
	struct film_s *n;
	
	if ( !fb ) return;
	head = &fb->filmy;
	
    list_for_each_safe(pnt, n, head) {
        list_del(pnt, head);
        delete pnt;
	}
}

/**
 * @brief filmbaza_rate oceń film
 * @param fb wskaxnik do bazy filmóœ
 * @param id id flimu
 * @param rate ocena filmu
 */
void filmbaza_rate(struct filmbaza *fb, int id, int rate)
{
	struct film_s *pnt;
	pnt = filmbaza_find_film_id(fb, id);
	if ( !pnt )
		return;
	pnt->rate = rate;
}
/**
 * @brief filmbaza_share pożycz komuś film
 * @param fb wskaxnik do bazy filmów
 * @param id id filmu
 * @param friend_name imie kogoś, komu pożyczamy film
 */
void filmbaza_share(struct filmbaza *fb, int id, std::string friend_name)
{
	struct film_s *pnt;
	pnt = filmbaza_find_film_id(fb, id);
	if ( !pnt )
		return;
	pnt->share.shared = 1;
	pnt->share.name = friend_name;
}
/**
 * @brief zwrów wypożyczony film
 * @param fb wskaxnik do bazy filmów
 * @param id id filmu wypożyczonego
 */
void filmbaza_unshare(struct filmbaza *fb, int id)
{
	struct film_s *pnt;
	pnt = filmbaza_find_film_id(fb, id);
	if ( !pnt )
		return;
	pnt->share.shared = 0;
	pnt->share.name = "";
}
/**
 * @brief porównuje dwa filmy względem ich id
 * @param priv jeśli priv różne od NULL, to sortowanie jest odwrotne
 * @param fa structura film_s w pierwszym filmie
 * @param fb structura film_s w drugim filmiw
 * @return mniejsze od zera jeśli @fa ma być przed @fb, większe jeśli @fa ma być po @fb
 */
int filmbaza_cmp_id(void *priv, struct film_s *fa, struct film_s *fb)
{
	const int a = fa->id;
	const int b = fb->id;
	return ( priv?+1:-1 )*( b - a );
}
/**
 * @brief porównuje dwa filmy względem ich nazwy
 * @param priv jeśli priv różne od NULL, to sortowanie jest odwrotne
 * @param fa structura film_s w pierwszym filmie
 * @param fb structura film_s w drugim filmiw
 * @return mniejsze od zera jeśli @fa ma być przed @fb, większe jeśli @fa ma być po @fb
 */
int filmbaza_cmp_name(void *priv, struct film_s *fa, struct film_s *fb)
{
	const std::string a = fa->name;
	const std::string b = fb->name;
	return ( priv?-1:+1 )*( strcasecmp(a.c_str(), b.c_str()) );
}
/**
 * @brief porównuje dwa filmy względem ich oceny
 * @param priv jeśli priv różne od NULL, to sortowanie jest odwrotne
 * @param fa structura film_s w pierwszym filmie
 * @param fb structura film_s w drugim filmiw
 * @return mniejsze od zera jeśli @fa ma być przed @fb, większe jeśli @fa ma być po @fb
 */
int filmbaza_cmp_rate(void *priv, struct film_s *fa, struct film_s *fb)
{
	const int a = fa->rate;
	const int b = fb->rate;
	return ( priv?-1:+1 )*( b - a );
}
/**
 * @brief porównuje dwa filmy względem czy są wypożyczone
 * @param priv jeśli priv różne od NULL, to sortowanie jest odwrotne
 * @param fa structura film_s w pierwszym filmie
 * @param fb structura film_s w drugim filmiw
 * @return mniejsze od zera jeśli @fa ma być przed @fb, większe jeśli @fa ma być po @fb
 */
int filmbaza_cmp_share_shared(void *priv, struct film_s *fa, struct film_s *fb)
{
	const int a = fa->share.shared;
	const int b = fb->share.shared;
	return ( priv?-1:+1 )*( b - a );
}
/**
 * @brief porównuje dwa filmy względem komu je wypozyczyliśmy
 * @param priv jeśli priv różne od NULL, to sortowanie jest odwrotne
 * @param fa structura film_s w pierwszym filmie
 * @param fb structura film_s w drugim filmiw
 * @return mniejsze od zera jeśli @fa ma być przed @fb, większe jeśli @fa ma być po @fb
 */
int filmbaza_cmp_share_name(void *priv, struct film_s *fa, struct film_s *fb)
{
	const std::string a = fa->share.name;
	const std::string b = fb->share.name;
	return ( priv?+1:-1 )*( strcasecmp(a.c_str(), b.c_str()) );
}
/**
 * @brief sortuje filmy
 * opt powinnien przyjąc jedną z wartości FILMBAZA_SORT_XXX
 * gdzie XXX oznacza po czym sortujemy w strukturze.
 * sprawdz filmbaza.h aby zobaczyć definicje makr FILMBAZA_SORT_XXX
 *
 * @param fb wskaxnik do bazy danych filmów
 * @param opt jeden z FILMBAZA_SORT_XXX
 * @param direction jeśli równe 0 to rosnąco, jeśli różne od 0 to sortowanie malejąco
 */
void filmbaza_sort(struct filmbaza *fb, int opt, int direction)
{
	int (*func)(void*, struct film_s*, struct film_s*);
	if ( !fb ) return;
	switch (opt) {
	case FILMBAZA_SORT_ID:
		func = filmbaza_cmp_id;
		break;
	case FILMBAZA_SORT_NAME:
		func = filmbaza_cmp_name;
		break;
	case FILMBAZA_SORT_RATE:
		func = filmbaza_cmp_rate;
		break;
	case FILMBAZA_SORT_SHARE_SHARED:
		func = filmbaza_cmp_share_shared;
		break;
	case FILMBAZA_SORT_SHARE_NAME:
		func = filmbaza_cmp_share_name;
		break;
	default:
		return;
	}
	if ( direction ) {
		list_sort(NULL, &fb->filmy, func);
	} else {
		/* never ever try to do something with that pointer */
		list_sort((void *)1, &fb->filmy, func);
	}
}

